#!/usr/bin/ruby

load 'comm.rb'

#游戏类
class Game
	#attr_reader :player
	attr_reader :market
  
	alias :org_puts :puts
	alias :org_gets :gets
	alias :org_printf :printf
	
	prepend ThreadIO
  
	def initialize
		@market = FtStockMarket.new(16,8)
		#@player = Player.new("hopy")
		@player_threads = {}
		@ct = Ctxt.new
	end

	def show_use_paper_tag_menu(player)
		#puts "which paper_tag to use? : \n" + player.str_pts + "\n"
		puts("#{player.str_pts}")
		printf "Which paper_tag?[number|<enter> for no]"
	end
  
	def wrong_choose
		#puts "\awrong choose , try again..."
		puts @ct.color_str("\awrong choose!please try again...",Ctxt::RED)
	end
  
	def show_one_trade_gain(cash,money_add)
		if money_add < 0
			ret = "亏鸟"
			sh_opt = Ctxt::B_RED
		elsif money_add > 0
			ret = "赚鸟"
			sh_opt = Ctxt::B_GREEN
		else
			ret = "保鸟"
			sh_opt = Ctxt::B_WHITE
		end

		s0 = "#{ret} #{money_add} "
		#print "你 ";@ct.color(s0,sh_opt);print "元在这一盘 , 你目前的现金为 #{cash} 元。\n"
		printf "你 #{@ct.color_str(s0,sh_opt)} 元在这一盘 , 你目前的现金为 #{cash} 元。\n"
	end
	
	#返回[c1,c2]
	#如果不满足使用条件则原样返回[c1,c2]
	def use_pt(pt_idx,c1,c2,stock,player)
		pt = player.pts[pt_idx][0]
		
		c1,c2,is_used = pt.use(c1,c2,stock)
		if is_used
			player.use_paper_tag(pt_idx)
		end
		[c1,c2]
	end
	
	#返回nil表示不用pt或没有pt可用
	#否则返回pt在pts中的序号
	def use_which_pt?(player)
		if player.pts_count == 0
			return nil
		end

		loop do
			show_use_paper_tag_menu(player)
			str = gets
			if str =~ /^(\d)+$/ && player.pts[str.to_i - 1]
				idx = str.to_i - 1
				if player.pts[idx][1] <= 0
					puts("pt剩余数为0,请换一种pt使用!");next
				end
				#player.use_paper_tag(str.to_i - 1)
				break idx
			elsif str =~ /^\n$/
				break nil
			else
				wrong_choose;next
			end
		end
	end
	
	def want_double?(rates)
		loop do
			printf("You had rats:[#{rates.join(" * ")}==#{rates.inject {|s,i|s*i}}]Go on?[y or <enter> for no]")
			case gets
				when /^y\n$/i
					return true
				when /^\n$/i
					return false
				else
					wrong_choose;next
			end
		end
	end
  
	def diff_2_cards(c1,c2,money)
		money_add = 0
		case c2
			when "大鬼"
				money_add = money * Pokers.val(c1)
			when "小鬼"
				money_add = -1*(money * Pokers.val(c1))
			else
				diff = Pokers.val(c2) - Pokers.val(c1)
				puts("#{Pokers.color_str(c2)} - #{Pokers.color_str(c1)} diff_val is #{diff}")
				money_add = money * diff
		end
		money_add
	end
	
	def last_rate(rates)
		rates.inject {|s,i|s * i}
	end

	def set_ctx_when_trade_over(player,add)
		ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
		player.set_ctx_one_trade(ctx)
	end

	def set_ctx_trading(player,last,c1,c2,w_upt,w_2,rate=nil)
		ctx = {over:false,ing:true,add:nil,last:last,c1:c1,c2:c2,rate:nil,w_2:w_2,w_upt:w_upt}
		player.set_ctx_one_trade(ctx)
	end

	def when_trade_over(player,money,rates)
		puts("rates is #{rates.join(" * ")} == #{last_rate(rates)}")
		add = money * last_rate(rates)
		player.cash += add
		show_one_trade_gain(player.cash,add)
		set_ctx_when_trade_over(player,add)	
	end
	
	#一共3次回调自身
	def one_trade(card,player,stock,money,rates=[1])
		if stock.count == 0
			unless card
				rate = 0
			else
				rate = Pokers.v1(card)
			end
			rates << rate	
			when_trade_over(player,money,rates)
		elsif stock.count == 1
			c1 = stock.pick_card("turning last card...")
			one_trade(c1,player,stock,money,rates)								#1st call self
		elsif stock.count >= 2			
			c1 = stock.pick_card("last (#{stock.count})turning 1st card...")
			c2 = nil	
			set_ctx_trading(player,@market.counts,c1,c2,true,false)
			if pt_idx = use_which_pt?(player)
				c1,c2 = use_pt(pt_idx,c1,c2,stock,player)
			end
			
			#如果c2已经产生,则可能会出现3张牌相同的情况,这时c2为最后一张牌,计算如下:
			#c2.v1 * c1.val * c1.val * c1.val * 剩余rates
			if c2
				last_r = rates[-1]
				#rates.coun > 1 避免rates=[1],而c1.val == 1的情况
				if Pokers.val(c1) == last_r && rates.count > 1
					puts("BINGOOO!!!你连续抽到3张同值的card")
					rates << last_r << last_r << Pokers.v1(c2)
					return when_trade_over(player,money,rates)
				end
			end
			
			if stock.count == 0
				unless c2
					return one_trade(c1,player,stock,money,rates)		#2nd call self
				else
					return when_trade_over(player,diff_2_cards(c1,c2,money),rates)
				end
			end
			
			unless c2
				c2 = stock.pick_card("last (#{stock.count})turning 2nd card...")
			end
			set_ctx_trading(player,@market.counts,c1,c2,true,false)
			if pt_idx = use_which_pt?(player)
				c1,c2 = use_pt(pt_idx,c1,c2,stock,player)
			end
			
			#c2此时不可能为nil,再次检查3张牌可能相同的情况
			raise "c2 == nil" unless c2
			if c2
				last_r = rates[-1]
				#rates.coun > 1 避免rates=[1],而c1.val == 1的情况
				if Pokers.val(c1) == last_r && rates.count > 1
					puts("BINGOOO!!!你连续抽到3张同值的card")
					rates << last_r << last_r << Pokers.v1(c2)	
					return when_trade_over(player,money,rates)
				end
			end
			
			if stock.count == 0	
				return when_trade_over(player,diff_2_cards(c1,c2,money),rates)
			end
			
			if Pokers.val(c1) == Pokers.val(c2)
				rate = Pokers.val(c1)	
				set_ctx_trading(player,@market.counts,c1,c2,false,true,rate)
				if want_double?(rates)
					rates << rate
					return one_trade(nil,player,stock,money,rates)			#3st call self
				end
			end
			when_trade_over(player,diff_2_cards(c1,c2,money),rates)
		end
	end

	#一共3次回调自身
	def one_trade_org(card,player,stock,money,rates=[1])
		if stock.count == 0
			unless card
				rate = 0
			else
				rate = Pokers.v1(card)
			end
			rates << rate
			puts("rates is #{rates.join(" * ")} == #{rates.inject {|s,i|s*i}}")
			add = money * rates.inject {|s,i|s*i}
			player.cash += add
			show_one_trade_gain(player.cash,add)
			ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
			player.set_ctx_one_trade(ctx)
		elsif stock.count == 1
			c1 = stock.pick_card("turning last card...")
			one_trade(c1,player,stock,money,rates)								#1st call self
		elsif stock.count >= 2
			#FIXME:for debug print:
			c1 = stock.pick_card("last (#{stock.count})turning 1st card...")
			c2 = nil
			ctx = {over:false,ing:true,add:nil,last:@market.counts,c1:c1,c2:c2,rate:nil,w_2:false,w_upt:true}
			player.set_ctx_one_trade(ctx)
			if pt_idx = use_which_pt?(player)
				c1,c2 = use_pt(pt_idx,c1,c2,stock,player)
			end
			
			#如果c2已经产生,则可能会出现3张牌相同的情况,这时c2为最后一张牌,计算如下:
			#c2.val * c1.val * c1.val * c1.val * 剩余rates
			if c2
				last_rate = rates[-1]
				#rates.coun > 1 避免rates=[1],而c1.val == 1的情况
				if Pokers.val(c1) == last_rate && rates.count > 1
					puts("BINGOOO!!!你连续抽到3张同值的card")
					rates << last_rate
					rates << last_rate
					
					rate = Pokers.v1(c2)
					rates << rate
					puts("rates is #{rates.join(" * ")} == #{rates.inject {|s,i|s*i}}")
					add = money * rates.inject {|s,i|s*i}
					player.cash += add
					ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
					player.set_ctx_one_trade(ctx)
					return show_one_trade_gain(player.cash,add)
					#return one_trade_ai(c2,player,stock,money,rates)
				end
			end
			
			if stock.count == 0
				unless c2
					return one_trade(c1,player,stock,money,rates)		#2nd call self
				else
					puts("rates is #{rates.join(" * ")} == #{rates.inject {|s,i|s*i}}")
					add = diff_2_cards(c1,c2,money) * rates.inject {|s,i|s*i}
					player.cash += add
					ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
					player.set_ctx_one_trade(ctx)
					return show_one_trade_gain(player.cash,add)
				end
			end
			
			unless c2
				c2 = stock.pick_card("last (#{stock.count})turning 2nd card...")
			end
			ctx = {over:false,ing:true,add:nil,last:@market.counts,c1:c1,c2:c2,rates:nil,w_2:false,w_upt:true}
			player.set_ctx_one_trade(ctx)
			if pt_idx = use_which_pt?(player)
				c1,c2 = use_pt(pt_idx,c1,c2,stock,player)
			end
			
			#c2此时不可能为nil
			#再次检查3张牌可能相同的情况
			raise "c2 == nil" unless c2
			if c2
				last_rate = rates[-1]
				#rates.coun > 1 避免rates=[1],而c1.val == 1的情况
				if Pokers.val(c1) == last_rate && rates.count > 1
					puts("BINGOOO!!!你连续抽到3张同值的card")
					rates << last_rate
					rates << last_rate
					
					rate = Pokers.v1(c2)
					rates << rate
					puts("rates is #{rates.join(" * ")} == #{rates.inject {|s,i|s*i}}")
					add = money * rates.inject {|s,i|s*i}
					player.cash += add
					ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
					player.set_ctx_one_trade(ctx)
					return show_one_trade_gain(player.cash,add)
					#return one_trade_ai(c2,player,stock,money,rates)			
				end
			end
			
			if stock.count == 0
				puts("rates is #{rates.join(" * ")} == #{rates.inject {|s,i|s*i}}")
				add = diff_2_cards(c1,c2,money) * rates.inject {|s,i|s*i}
				#add = diff_2_cards(c1,c2,money)
				player.cash += add
				ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
				player.set_ctx_one_trade(ctx)
				return show_one_trade_gain(player.cash,add)
			end
			
			if Pokers.val(c1) == Pokers.val(c2)
				rate = Pokers.val(c1)
				ctx = {over:false,ing:true,add:nil,last:@market.counts,c1:nil,c2:nil,rate:rate,w_2:true,w_upt:false}
				player.set_ctx_one_trade(ctx)
				if want_double?(rates)
					rates << rate
					return one_trade(nil,player,stock,money,rates)			#3st call self
				end
			end
			puts("rates is #{rates.join(" * ")} == #{rates.inject {|s,i|s*i}}")
			add = diff_2_cards(c1,c2,money) * rates.inject {|s,i|s*i}
			#add = diff_2_cards(c1,c2,money)
			player.cash += add
			show_one_trade_gain(player.cash,add)
			ctx = {over:true,ing:false,add:add,last:nil,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
			player.set_ctx_one_trade(ctx)
		end
	end
	
	def sh_main_menu(player)
		puts "1:buy_stock 2:buy_paper_tag 3:state n:quit\n#{player.name}>"
	end

	def sh_buy_stock_menu
		puts @market.str_stocks + " n:ret_main_menu (stk_NO money or n) : "
	end
  
	def sh_buy_paper_tag_menu(player)	
		str = ""
		PAPER_TAGS.each_with_index do |pt,i|
			str << "#{i+1}:#{pt} "
		end
		str << "n:ret (#{player.cash} py)"
		puts str
		printf "#{player.name}>"
	end
  
	def bind(player)
		t = play_thread(player,*IO.pipe)
		@player_threads[t.object_id] = t
		#FIXME:如果不延时则t[:]会返回nil
		sleep 0.1
		t[:w]
	end
  
	def when_choose_menu_buy_stock(player)
		loop do
			sh_buy_stock_menu
			ctx = {over:true,ing:false,add:nil,last:@market.counts,c1:nil,c2:nil,rate:nil,w_2:false,w_upt:false}
			player.set_ctx_one_trade(ctx)
			case ret = gets
				when /^\d \d|\*$/
					stock_id,money = ret.split(" ")
					money = money == "*" ? player.cash : money.to_i
					if money > player.cash
						puts("你的投入的money超出了你的总现金！");next
					end
					idx = stock_id.to_i - 1
					raise "不可以用负数引用股票!" if idx < 0
					stock = @market.stocks[idx]
					raise "该股不存在!" unless stock
					one_trade(nil,player,stock,money)
					raise "你已经没有现金了!" if player.cash <= 0
				when /^n$/i;break
				else
					wrong_choose;next
			end
		end
	end
	
	def when_choose_menu_buy_pt(player)
		loop do
			raise "你已经没有现金了!" if player.cash <= 0
			sh_buy_paper_tag_menu(player)
			#捕获n,1,1*5,1  * 5等等
			r = /^(n$|\d+)(?:[[:blank:]]*\*[[:blank:]]*(\d+))*$/i
			ret = r.match(gets)
			(wrong_choose;next) unless ret
			break if ret[1] =~ /n/i
			
			idx = ret[1].to_i - 1
			(wrong_choose;next) if idx + 1 > PAPER_TAGS.count
			if ret[2]
				count = ret[2].to_i
			else
				count =1
			end
			
			pt = PAPER_TAGS[idx]
			#pt一定是远程对象,这样的pt对象调用use方法是远程的方法!
			#否则pt的use方法中的puts会在客户端执行,这样Thread.current[:player].w为nil
			#结果所有puts,printf均会出错!
			player.buy_pts(pt,count)
		end
	end
	
	def play(player)
		loop do
			(puts "no enough money,game over!";break) if player.cash <= 0
			sh_main_menu(player)
			case gets
				when /^1$/
					when_choose_menu_buy_stock(player)
				when /^2$/
					when_choose_menu_buy_pt(player)
				when /^3$/
					puts player.to_s
				when /^n$/i
					return
				else
				wrong_choose;next
			end
		end
	end
  
	def play_loop(player,r)
		#$stdin,$stdout = r,player.w
		play(player)
	rescue =>e
		puts "#{__callee__}:#{e.to_s}"
		#FIXME:调试使用下面一行代码
		#err(e)
		return nil if player.cash <= 0
		retry
	ensure
		#$stdin,$stdout = STDIN,STDOUT
	end
 
	def play_thread(player,r,w)
		Thread.new(player,r,w) do |player,r,w|
			t = Thread.current
			t[:w] = w
			t[:r] = r
			t[:player] = player
			#player.init_pts_r
			#player.pts = []
			#PAPER_TAGS.each {|pt|player.pts << [pt,0]}
			#org_puts "==>#{player.pts}"

			org_puts "bind with player : #{player.name} ..."
			player.w.write("hello #{player.name},your cash is #{player.cash},be happy!!!\n")
		  
			begin
				play_loop(player,r)
				if player.cash <= 0
					player.w.puts("bye #{player.name},at last your cash is #{player.cash}\n\n")
				else
					player.w.puts("bye disconnected the game!\n\n")
				end
				org_puts("unbind with player: #{player.name}")
			rescue IO::EAGAINWaitReadable =>e
				nil
			end
		end
	end
  
  #清除已经死亡的player线程
	def clear_dead_thread
		@player_threads.delete_if {|k,t|!t.alive?}
	end
  
end
